﻿// See https://aka.ms/new-console-template for more information

using CodeAgent.Function;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Agents.Chat;
using Microsoft.SemanticKernel.Agents.OpenAI;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;

#pragma warning disable OPENAI001

#pragma warning disable SKEXP0001

#pragma warning disable SKEXP0110

#pragma warning disable SKEXP0010

internal class Program
{
    public static async Task Main(string[] args)
    {
        var model = "gpt-4o";

        var kernelBuilder = Kernel.CreateBuilder();

        kernelBuilder.Plugins.AddFromType<UserFunction>();
        kernelBuilder.Plugins.AddFromType<CodeFunction>();

        kernelBuilder.AddOpenAIChatCompletion(model, new Uri("https://api.token-ai.cn/v1"),
            await File.ReadAllTextAsync("token"));

        var kernel = kernelBuilder.Build();

        var codeReviewAgent = CreateCodeReviewAgent(kernel);
        var programmerAgent = CreateProgrammerAgent(kernel);
        var codeErrorCheckAgent = CreateCodeErrorCheckAgent(kernel);


        var chat = new AgentGroupChat(codeReviewAgent, programmerAgent, codeErrorCheckAgent)
        {
            ExecutionSettings = new AgentGroupChatSettings()
            {
                TerminationStrategy = new CodeTerminationStrategy()
                {
                    Agents = [codeReviewAgent],
                    MaximumIterations = 10,
                }
            }
        };

        var chatHistory = new ChatMessageContent(AuthorRole.User, "使用Avalonia实现一个简单的计算器,工作目录：D:\\agent");

        chat.AddChatMessage(chatHistory);

        WriteAgentChatMessage(chatHistory);

        await foreach (var item in chat.InvokeAsync())
        {
            WriteAgentChatMessage(item);
        }
    }

    public sealed class CodeTerminationStrategy : TerminationStrategy
    {
        protected override Task<bool> ShouldAgentTerminateAsync(Agent agent, IReadOnlyList<ChatMessageContent> history,
            CancellationToken cancellationToken)
        {
            // 如果最后一条消息包含[done]则终止

            return history[^1].Content?.Contains("[done]") == true ? Task.FromResult(true) : Task.FromResult(false);
        }
    }

    public static void WriteAgentChatMessage(ChatMessageContent messages)
    {
        var authorExpression = messages.Role == AuthorRole.User ? string.Empty : $"- {messages.AuthorName ?? "*"}";

        var contentExpression = string.IsNullOrWhiteSpace(messages.Content) ? string.Empty : messages.Content;
        var isCode = messages.Metadata?.ContainsKey(OpenAIAssistantAgent.CodeInterpreterMetadataKey) ?? false;

        var codeMarker = isCode ? "\n [CODE]\n" : string.Empty;

        Console.ForegroundColor = messages.Role == AuthorRole.User ? ConsoleColor.Green : ConsoleColor.Blue;
        Console.Write($"\n [{messages.Role}]:");
        Console.ResetColor();
        Console.WriteLine($"{authorExpression} {codeMarker} {contentExpression}");

        foreach (var item in messages.Items)
        {
            switch (item)
            {
                case AnnotationContent annotation:
                    Console.WriteLine($"  [{item.GetType().Name}] {annotation.Quote}: File #{annotation.FileId}");
                    break;
                case FileReferenceContent fileReference:
                    Console.WriteLine($"  [{item.GetType().Name}] File #{fileReference.FileId}");
                    break;
                case ImageContent image:
                    Console.WriteLine(
                        $"  [{item.GetType().Name}] {image.Uri?.ToString() ?? image.DataUri ?? $"{image.Data?.Length} bytes"}");
                    break;
                case FunctionCallContent functionCall:
                    Console.WriteLine($"  [{item.GetType().Name}] {functionCall.Id}");
                    break;
                case FunctionResultContent functionResult:
                    Console.WriteLine(
                        $"  [{item.GetType().Name}] {functionResult.CallId} - {functionResult.Result ?? "*"}");
                    break;
            }
        }
    }

    public static ChatCompletionAgent CreateCodeReviewAgent(Kernel kernel)
    {
        const string reviewAgentName = "CodeReviewAgent";
        const string reviewInstruction =
            """
            你是CodeReviewAgent,
            - 你的职责是检查代码问题，只需要检查代码的质量，不需要编写代码。
            - 如果给出的代码完全符合预期，那么只回复：[done]。
            - 如果不符合用户的需求，你需要提出建议，并且给出解决方案的思路,提供思维链的一系列解决思路,但是不要直接给出答案。
            - 你还需要将用户的问题提供一些思维链的计划，然后告知ProgrammerAgent,但是你不能直接[done]。
            """;
        var codeReviewAgent = new ChatCompletionAgent()
        {
            Name = reviewAgentName,
            Instructions = reviewInstruction,
            Kernel = kernel,
            Description = "代码审查机器人",
            Arguments = new KernelArguments(new OpenAIPromptExecutionSettings()
                { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
        };

        return codeReviewAgent;
    }

    /// <summary>
    /// 检查代码是否存在报错
    /// </summary>
    /// <returns></returns>
    public static ChatCompletionAgent CreateCodeErrorCheckAgent(Kernel kernel)
    {
        const string errorCheckAgentName = "CodeErrorCheckAgent";
        const string errorCheckInstruction =
            """
            你是CodeErrorCheckAgent,
            - 你的职责是检查代码是否存在报错，只需要检查代码的质量，不需要编写代码。
            - 如果代码构建没问题，那么只回复：[yes-code]。
            - 如果代码构建出现异常，那么你需要告知ProgrammerAgent，然后提供解决方案的思路。
            """;
        var codeErrorCheckAgent = new ChatCompletionAgent()
        {
            Name = errorCheckAgentName,
            Instructions = errorCheckInstruction,
            Kernel = kernel,
            Description = "代码错误检查机器人",
            Arguments = new KernelArguments(new OpenAIPromptExecutionSettings()
                { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
        };

        return codeErrorCheckAgent;
    }

    /// <summary>
    /// 创建一个程序员Agent
    /// </summary>
    /// <returns></returns>
    public static ChatCompletionAgent CreateProgrammerAgent(Kernel kernel)
    {
        const string programmerAgentName = "ProgrammerAgent";
        const string programmerInstruction =
            """
            你是ProgrammerAgent,
            - 你的职责是编写代码，
            - 请根据用户的需求编写代码，
            - 尽可能考虑到最佳实践，并且保持代码的可读性。
            - 您还可以帮助用户直接创建项目，修改代码文件,如果需要构建项目，通知CodeErrorCheckAgent 检查代码是否存在报错。
            """;

        var programmerAgent = new ChatCompletionAgent()
        {
            Name = programmerAgentName,
            Instructions = programmerInstruction,
            Kernel = kernel,
            Description = "程序员机器人",
            Arguments = new KernelArguments(new OpenAIPromptExecutionSettings()
                { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }),
        };

        return programmerAgent;
    }
}